home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Reference Guide / C-C++ Interactive Reference Guide.iso / c_ref / clesson / clesson.dir / 00050_Field_50.txt < prev    next >
Text File  |  1995-04-04  |  19KB  |  547 lines

  1.  
  2.                                      Lesson 7
  3.  
  4.                              De-bugging Strategies.
  5.  
  6.       Proper Preparation Prevents Piss-Poor Performance. 
  7.  
  8.   This lesson is really a essay about how to go about writing programs.
  9.  
  10.   I know that by far the best way to greatly reduce the amount of effort
  11. required to get a program going properly is to avoid making mistakes in the first palace! Now this might seem to be stating the absolute obvious, and it is but after looking at many programs it would seem that there is a very definite need to say it.
  12.  
  13.   So how does one go about reducing the probability of making mistakes?
  14.  
  15.  
  16.  
  17.   There are many strategies, and over the years I have evolved my own set. I have found that some of the most important are:
  18.  
  19.   1) Document what you will do before yes BEFORE you write any code.
  20.     Set up the source files for the section of the program you are going to
  21.      write and put some lines of explanation as to what you intend to do in
  22.      this file. Be as precise as you can, but don't go into the detail of
  23.      explaining in English, or your First Language, exactly what every
  24.      statement does.
  25.  
  26.   2) Make sure that you keep each file as small as is sensible. Program
  27.      authors say that one should put only one function in a file. It's my
  28.      personal opinion that this is going a little bit over the top, but
  29.      certainly you should not have more than one logical activity in 
  30.      a  source file. It's easier to find a needle in a tiny haystack than in 
  31.      a big one!
  32.  
  33.  
  34.  
  35.  
  36.   3) Always use names for the objects in your program which are fully
  37.      descriptive, or at are meaningful nmemonics. Put yourself in 
  38.     the position of some poor soul who - a couple of years later, after you
  39.      have long finished with the project, and left the country - has 
  40.      been given the task of adding a small feature to your exquisite
  41.      program. Now in the rush to get your masterpiece finished 
  42.      you decided to use variable names
  43.      like "a4" and "isb51" simply so that you can get the line typed a
  44.      fraction of a second faster than if you used something like
  45.      "customer_address[POST_CODE]" and 
  46.      "input_status_block[LOW_FUEL_TANK_#3].
  47.      The difference in ease of understanding is obvious, isn't it? However
  48.      judging by some programs which I have seen published in both
  49.      magazines and in the public domain program sources, the point has
  50.      still to be made.
  51.  
  52.  
  53.  
  54.   4) ALWAYS take great care with the layout of your code  It's my
  55.       opinion that the opening brace of ALL program structures should
  56.      be on a new line. Also if put them in the leftmost column for structs,
  57.      enums, and initialised tables, as well as functions, then the
  58.      'find function' keystrokes ( "[[" and "]]" ) in vi will find them as well
  59.      as the functions themselves. Make sure the "showmatch" facility
  60.      in vi turned on. ( And watch the cursor jump when you enter the
  61.      right hand brace, bracket, or parenthesis. )
  62.  
  63.   5) Try as hard as you can to have as few global variables as possible.
  64.      Some people say never have any globals. This is perhaps a bit too
  65.      severe but global variables are a clearly documented source of
  66.      programming errors. If it's impossible to perform a logical activity
  67.      in an efficient way without having a global or two, then confine
  68.      the scope of the globals to just the one file by marking the defining
  69.      declaration "static". It stops the compiler producing a symbol which
  70.      the linking loader will make available to all the files in your source.
  71.  
  72.  
  73.   6) Never EVER put 'magic numbers' in you source code. Always 
  74.       define constants in a header file with #define lines or enum
  75.       statements.
  76.  
  77.      Here is an example:-
  78.  
  79. /* ----------------------------------------- */
  80.  
  81. #include <stdio.h>
  82.  
  83. enum status_input_names
  84. {
  85.   radiator_temperature,
  86.   oil_temperature,
  87.   fuel_pressure,
  88.   energy_output,
  89.   revolutions_per_minute
  90.   };
  91.  
  92.  
  93.  
  94. char *stats[] =
  95. {
  96.   "radiator_temperature",
  97.   "oil_temperature",
  98.   "fuel_pressure",
  99.   "energy_output",
  100.   "revolutions_per_minute"
  101.   };
  102.  
  103. #define NUMBER_OF_INPUTS ( sizeof ( stats ) / sizeof ( stats[0]))
  104.  
  105. main()
  106. {
  107.   enum status_input_names name;
  108.  
  109.  
  110.  
  111.  
  112.   printf ( "Number of Inputs is: %d\n", NUMBER_OF_INPUTS );
  113.   for ( name = radiator_temperature; name < NUMBER_OF_INPUTS; name++)
  114.   {
  115.     printf ( "\n%s", stats[ name ] );
  116.     }
  117.   printf ( "\n\n" );
  118.   }
  119.  
  120. /* ----------------------------------------- */
  121.  
  122.   Note that as a side effect we have available the meaningful symbols
  123.   radiator_temperature etc. as indices into the array of status input names and the symbol NUMBER_OF_INPUTS available for use as a terminator in the 'for' loop. This is quite legal because sizeof is a pseudo-function and the value is evaluated at the time of compilation
  124.  
  125.  
  126.   and not when the program is executed. This means that the result 
  127.   of the division in the macro is calculated at the time of compilation 
  128.   and this result is used as a literal  in the 'for' loop. No division takes
  129.   place each time the loop is executed.
  130.  
  131.   To illustrate the point I would like to tell you a little story which is
  132.   fictitious, but which has a ring of truth about it.
  133.   Your employer has just landed what seems to be a lucrative contract
  134.   with an inventor of a completely new type of engine. We are assured
  135.   that after initial proving trials one of the larger Japanese motor
  136.   manufactures is going to come across with umpteen millions to
  137.   complete the development of  the design. You are told to write a
  138.   program which has to be a simple and
  139.   straightforward exercise in order to do the job as cheaply as possible.
  140.   Now, the customer - a some-what impulsive type - realises that his
  141.   engine is not being monitored closely enough when it starts to rapidly
  142.   dis-assemble itself under high speed and heavy load. You have to add 
  143.  
  144.  
  145.   few extra parameters to the monitoring program by yesterday morning!
  146.   You just add the extra parameters into the enumand the array of pointers to the character strings. So:
  147.  
  148. enum status_input_names
  149. { radiator_temperature,
  150.   radiator_pressure,
  151.   fuel_temperature,
  152.   fuel_pressure,
  153.   oil_temperature,
  154.   oil_pressure,
  155.   exhaust_manifold_temperature
  156.   };
  157.  
  158.   Let's continue the story about the Japanese purchase. Mr. Honda ( jun ) hascome across with the money and the result is that you are now a team leader in the software section of Honda Software ( YourCountry ) Ltd. The project of which you are now leader is to completely rewrite
  159.  
  160.  
  161.  
  162.  your monitoring program and add a whole lot of extra channels as well as to make the printouts much more readable so that your cheap, cheerful, and aesthetic-free program can be sold as the "Ultimate Engine Monitoring Package" from the now world famous Honda
  163. Real-time Software Systems. You set to work, Honda et. al. imagine 
  164. that there is going to be a complete redesign of the software at a cost 
  165. of many million Yen. You being an ingenious type have written the 
  166. code so that it is easy to  enhance.
  167.  
  168.   The new features required are that the printouts have to be printed with the units of measure appended to the values which have to scaled and processed so that the number printed is a real physical value instead of the previous arrangement where the raw transducer output was just dumped onto a screen.
  169.  
  170.   What do you have to do?
  171.  
  172.  
  173.  
  174.   Thinking along the line of "Get the Data arranged correctly first".
  175.   You take you old code and expand it so that all the items of information
  176.   required for each channel are collected into a struct.
  177.  
  178. enum status_input_names
  179. {
  180.   radiator_temperature,
  181.   radiator_pressure,
  182.   fuel_temperature,
  183.   fuel_pressure,
  184.   oil_temperature,
  185.   oil_pressure,
  186.   exhaust_manifold_temperature,
  187.   power_output,
  188.   torque
  189.   };
  190.  
  191.  
  192.  
  193.  
  194. typedef struct channel
  195. {
  196.   char *name;               /* Channel Name to be displayed on screen. */
  197.   int nx;                    /* position of name on screen x co-ordinate.*/
  198.   int ny;                        /* ditto for y */
  199.   int unit_of_measure;           /* index into units of measure array */
  200.   char value;                    /* raw datum value from 8 bit ADC */
  201.   char lower_limit;              /* For alarms. */
  202.   char upper_limit;
  203.   float processed_value;         /* The number to go on screen. */
  204.   float offset;
  205.   float scale_factor;
  206.   int vx;                        /* Position of value on screen. */
  207.   int vy;
  208.   }CHANNEL;
  209.  
  210.  
  211.  
  212.  
  213. enum units_of_measure { kPa, degC, kW, rpm, Volts, Amps, Newtons };
  214.  
  215. char *units { "kPa", "degC", "kW", "rpm", "Volts", "Amps", "Newtons" };
  216.  
  217. CHANNEL data [] =
  218. {
  219.   { "radiator temperature",
  220.   { "radiator pressure",
  221.   { "fuel temperature",
  222.   { "fuel pressure",
  223.   { "oil temperature",
  224.   { "oil pressure",
  225.   { "exhaust manifold temperature",
  226.   { "power output",
  227.   { "torque",
  228.   };
  229.  
  230.  
  231.  
  232. #define NUMBER_OF_INPUTS sizeof (data ) / sizeof ( data[0] )
  233.  
  234. Now the lesson preparation is to find the single little bug in the above
  235. program fragment, to finish the initialisation of the data array of type
  236. CHANNEL and to have a bit of a crack at creating a screen layout
  237. program to display its contents. Hint: Use printf();
  238. ( Leave all the values which originate from the real world as zero. )
  239.  
  240.   Here are some more tips for young players.
  241.  
  242.   1) Don't get confused between the logical equality operator,
  243.  
  244.      ==
  245.  
  246.      and the assignment to a variable operator.
  247.  
  248.      =
  249.  
  250.  
  251.  
  252.  
  253.      This is probably the most frequent mistake made by 'C' beginners, and  has the great disadvantage that, under most circumstances, the compiler  will quite happily accept your mistake.
  254.  
  255.   2) Make sure that you are aware of the difference between the logical
  256.      and bit operators.
  257.  
  258.      &&         This is the logical AND function.
  259.      ||         This is the logical OR function.
  260.                 The result is ALWAYS either a 0 or a 1.
  261.  
  262.      &          This is the bitwise AND function used for masks etc.
  263.                 The result is expressed in all the bits of the word.
  264.  
  265.  
  266.  
  267.  
  268.  
  269.   3) Similarly to 2 be aware of the difference between the logical
  270.      complementation and the bitwise one's complement operators.
  271.  
  272.      !          This is the logical NOT operator.
  273.      ~          This is the bitwise ones complement op.
  274.  
  275.      Some further explanation is required. In deference to machine efficiency a
  276.      LOGICAL variable is said to be true when it is non-zero. So let's set a
  277.      variable to be TRUE.
  278.  
  279.      00000000000000000000000000000001  A word representing TRUE.
  280.      Now let's do a logical NOT  !.
  281.      00000000000000000000000000000000  There is a all zero word, a FALSE.
  282.  
  283.  
  284.  
  285.      00000000000000000000000000000001  That word again. TRUE.
  286.      Now for a bitwise complement  ~.
  287.      11111111111111111111111111111110  Now look we've got a word which is non-zero, still TRUE.
  288.  
  289.      Is this what you intended?
  290.  
  291.   4) It is very easy to fall into the hole of getting the
  292.      '{' & '}'; '[' & ']'; '(' & ')'; symbol pairs all messed up and the
  293.      computer thinks that the block structure is quite different from that
  294.      which you intend. Make sure that you use an editor which tells you 
  295.      matching symbol. The UNIX editor vi does this provided that you turn
  296.      on the option. Also take great care with your layout so that the block
  297.      structure is absolutely obvious, and whatever style you choose do
  298.      take care to stick by it throughout the whole of the project.
  299.      A personal layout paradigm is like this:
  300.  
  301.  
  302.  
  303.   Example 1.
  304.  
  305. function_type function_name ( a, b )
  306. type a;
  307. type b;
  308. {
  309.   type variable_one, variable_two;
  310.  
  311.   if ( logical_expression )
  312.   {
  313.     variable_one = A_DEFINED_CONSTANT;
  314.     if ( !return_value = some_function_or_other ( a,
  315.                                                   variable_one,
  316.                                                   &variable_two
  317.                                                   )
  318.          )
  319.  
  320.  
  321.     {
  322.       error ( "function_name" );
  323.       exit ( FAILURE );
  324.       }
  325.     else
  326.     {
  327.       return ( return_value + variable_two );
  328.       }
  329.     }    /* End of "if ( logical_expression )" block */
  330.   }    /* End of function */
  331.  
  332.   This layout is easy to do using vi with this initialisation script
  333.   in either the environment variable EXINIT or the file ${HOME}/.exrc:-
  334.  
  335. set showmode autoindent autowrite tabstop=2 shiftwidth=2 showmatch wm=1
  336.  
  337.  
  338.  
  339.   Example 2.
  340.  
  341. void printUandG()
  342. {
  343.   char *format =
  344. "\n\
  345.            User is: %s\n\
  346.           Group is: %s\n\n\
  347.  Effective User is: %s\n\
  348. Effective Group is: %s\n\n";
  349.  
  350.   ( void ) fprintf ( tty,
  351.                      format,
  352.                      passwd_p->pw_name,
  353.                      group_p->gr_name,
  354.                      epasswd_p->pw_name,
  355.                      egroup_p->gr_name
  356.                      );
  357.  
  358.  
  359.  
  360.   }
  361.  
  362.   Notice how it is possible to split up format statements with a '\' as
  363.   the last character on the line, and that it is convenient to arrange
  364.   for a nice output format without having to count the
  365.   field widths. Note however that when using this technique that the '\'
  366.   character MUST be the VERY LAST one on the line. Not even a 
  367.   space may follow it!
  368.  
  369.   In summary I *ALWAYS* put the opening brace on a new line, set tabs
  370.   so that the indentation is just two spaces, ( use more and you quickly
  371.   run out of "line", especially on an eighty column screen ). If a statement
  372.   is too long to fit on a line I break the line up with the arguments set out
  373.   one to a line and I then the indentation rule to the parentheses "()".  
  374.   Sample immediately above. Probably as a hang-over from a particular
  375.   pretty printing program which reset the indentation position after the
  376.   printing of the closing brace "}", I am in the habit of doing it as well.
  377.  
  378.  
  379.   Long "if" and "for" statements get broken up in the same way. 
  380.  This is an example of it all. The fragment of code is taken from a 
  381.  curses oriented data input function.
  382.  
  383.   /*
  384.   ** Put all the cursor positions to zero.
  385.   */
  386.  
  387.   for ( i = 0;
  388.         s[i].element_name != ( char *) NULL &&
  389.         s[i].element_value != ( char *) NULL;
  390.         i = ( s[i].dependent_function == NULL )
  391.             ? s[i].next : s[i].dependent_next
  392.         )
  393.   {                              /* Note that it is the brace and NOT the    */
  394.                                  /* "for" which moves the indentation level. */
  395.     s[i].cursor_position = 0;
  396.     }
  397.  
  398.  
  399.  
  400.   /*
  401.   ** Go to start of list and hop over any constants.
  402.   */
  403.  
  404.     for ( i = edit_mode = current_element = 0;
  405.           s[i].element_value == ( char *) NULL ;
  406.           current_element = i = s[i].next
  407.           ) continue;                               /* Note EMPTY statement. */
  408.  
  409.   /*
  410.   ** Loop through the elements, stopping at end of table marker,
  411.   ** which is an element with neither a pointer to an element_name nor
  412.   ** one to a element_value.
  413.   */
  414.  
  415.   while ( s[i].element_name != ( char *) NULL &&
  416.           s[i].element_value != ( char *) NULL
  417.           )
  418.  
  419.  
  420.  
  421.   {
  422.     int c;           /* Varable which holds the character from the keyboard. */
  423.  
  424.     /*
  425.     **  Et Cetera for many lines.
  426.     */
  427.  
  428.     }
  429.  
  430.   Note the commenting style. The lefthand comments provide a general
  431. overview of what is happening and the righthand ones a more detailed view. The double stars make a good marker so it is easy to separate the code and the comments at a glance.
  432.  
  433.   The null statement.
  434.  
  435.  
  436.  
  437.   You should be aware that the ";" on it is translated by the compiler
  438. as a no-operation statement. The usefullness of this is that you can do
  439. little things, such as counting up a list of objects, or positioning a pointer
  440. entirely within a "for" or "while" statement. ( See example above ).
  441. There is, as always, a flip side. It is HORRIBLY EASY to put a ";" at the
  442. end of the line after the closing right parenthesis - after all you do just
  443. that for function calls! The suggestion is to both mark deliberate null
  444. statements with a comment and to use the statement "continue;". Using
  445. the assert macro will pick up these errors at run time.
  446.  
  447.   The assert macro.
  448.  
  449.   Refer to the Programmers Reference Manual section 3X and find the
  450. documentation on this most useful tool.
  451.  
  452.   As usual an example is by far the best wasy to explain it.
  453.  
  454.  
  455.  
  456. /* ----------------------------------------- */
  457.  
  458. #ident "@(#) assert-demo.c"
  459.  
  460. #include <stdio.h>
  461. #include <assert.h>
  462.  
  463. #define TOP_ROW 10
  464. #define TOP_COL 10
  465.  
  466. main()
  467. {
  468.   int row, col;
  469.  
  470.   for ( row = 1; row <= TOP_ROW; row++);
  471.  
  472.  
  473.  
  474.  
  475.   {
  476.     assert ( row <= TOP_ROW );
  477.     for ( col = 1; col <= TOP_COL; col++ )
  478.     {
  479.       assert ( col <= TOP_COL );
  480.       printf ( "%4d", row * col );
  481.       }
  482.     printf ( "\n" );
  483.     }
  484.   }
  485.  
  486. /* ----------------------------------------- */
  487.  
  488.   Which produces the output:-
  489.  
  490. Assertion failed:  row <= TOP_ROW , file assert-demo.c, line 15
  491. ABORT instruction (core dumped)
  492.  
  493.  
  494.  
  495.  
  496.  
  497.   It does this because the varable "row" is incremented
  498. to one greater than The value of TOP_ROW.
  499.  
  500.   Note two things:
  501.  
  502.   1) The sense of the logical condition. The assert is asserted
  503.      as soon as the result of the logical condition is FALSE.
  504.      Have a look at the file /usr/include/assert.
  505.      Where is the ";" being used as an empty program statement?
  506.  
  507.   2) The unix operating system has dumped out an image of the
  508.       executing program for examination using a symbolic debugger. 
  509.       Have a play with "sdb" in preparation for the lesson which 
  510.       deals with it in more detail.
  511.  
  512.  
  513.  
  514.  
  515.  
  516.   Lets remove the errant semi-colon, re-compile and re-run the program.
  517.  
  518.    1   2   3   4   5   6   7   8   9  10
  519.    2   4   6   8  10  12  14  16  18  20
  520.    3   6   9  12  15  18  21  24  27  30
  521.    4   8  12  16  20  24  28  32  36  40
  522.    5  10  15  20  25  30  35  40  45  50
  523.    6  12  18  24  30  36  42  48  54  60
  524.    7  14  21  28  35  42  49  56  63  70
  525.    8  16  24  32  40  48  56  64  72  80
  526.    9  18  27  36  45  54  63  72  81  90
  527.   10  20  30  40  50  60  70  80  90 100
  528.  
  529.   Here's the ten times multiplication table, for you to give to to
  530. the nearest primary-school child!
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.   I would agree that it is not possible to compare the value of a program
  542. layout with a real work of fine art such as a John Constable painting or
  543. a Michaelangelo statue, I do think a well laid out and literate example of
  544. programming is not only much easier to read and understand, but also it
  545. does have a certain aesthetic appeal.
  546.  
  547.